package gestionedt.controllers;

import gestionedt.controllers.exceptions.NonexistentEntityException;
import gestionedt.models.Creneau;
import gestionedt.models.Enseignant;
import gestionedt.models.Groupe;
import gestionedt.models.Module;
import gestionedt.models.Prestation;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import org.eclipse.persistence.config.HintValues;
import org.eclipse.persistence.config.QueryHints;

/**
 *
 *
 */
public class PrestationJpaController {

    public PrestationJpaController() {
        emf = Persistence.createEntityManagerFactory("GestionEDTPU");
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    public void create(Prestation prestation) {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.persist(prestation);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Prestation prestation) throws NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            prestation = em.merge(prestation);
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = prestation.getId();
                if (findPrestation(id) == null) {
                    throw new NonexistentEntityException("The prestation with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws NonexistentEntityException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Prestation prestation;
            try {
                prestation = em.getReference(Prestation.class, id);
                prestation.getId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The prestation with id " + id + " no longer exists.", enfe);
            }
            em.remove(prestation);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Prestation> findPrestationEntities() {
        return findPrestationEntities(true, -1, -1);
    }

    public List<Prestation> findPrestationEntities(int maxResults, int firstResult) {
        return findPrestationEntities(false, maxResults, firstResult);
    }

    private List<Prestation> findPrestationEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            Query q = em.createQuery("select object(o) from Prestation as o");
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Prestation findPrestation(Integer id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(Prestation.class, id);
        } finally {
            em.close();
        }
    }

    public int getPrestationCount() {
        EntityManager em = getEntityManager();
        try {
            return ((Long) em.createQuery("select count(o) from Prestation as o").getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

    /**
     * Récupère la liste des prestations qui se trouve (ou pourraient se trouver)
     * sur l'Emploi Du Temps affiché (c'est-à-dire celui pour lequel des groupes
     * ou des enseignants ont été sélectionnés, les identifiants de ces groupes
     * et enseignants sont passés en paramètres).
     * @param idsGrpSel identifiants des groupes sélectionnés pour apparaître sur l'EDT.
     * @param idsEnsSel identifiants des enseignants sélectionnés pour apparaître sur l'EDT.
     * @param sontCasees indique si les prestations sélectionnées doivent être déjà placées sur l'EDT ou non.
     * @return liste des prestations qui apparaissent ou pourraient apparaître sur l'EDT (avec
     * les modules, les groupes et les enseignants préchargés).
     */
    private List<Prestation> findPrestationsRelativesEDT(
            int[] idsGrpSel,
            int[] idsEnsSel,
            boolean sontCasees) {

        if(idsGrpSel.length == 0 && idsEnsSel.length == 0)
            return new ArrayList<Prestation>(0);

        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT DISTINCT p " +
                          "FROM Prestation AS p " +
                            "INNER JOIN p.idModule m " +
                            "LEFT JOIN p.groupeCollection g " +
                            "LEFT JOIN p.idEnseignant e " +
                            "INNER JOIN FETCH p.idModule " +
                            "INNER JOIN FETCH p.idEnseignant " +
                            "LEFT JOIN FETCH p.groupeCollection " +
                            "LEFT JOIN FETCH p.numCreneau " +
                          "WHERE m.estTermine = FALSE ";

            if(sontCasees)
                strQ += "AND p.numCreneau IS NOT NULL ";
            else
                strQ += "AND p.numCreneau IS NULL ";

            String critGrp = "g.id IN (?1";
            for(int i = 2; i <= idsGrpSel.length; i++)
                critGrp += (", ?"+i);
            critGrp += ")";

            String critEns = "e.id IN (?"+(idsGrpSel.length+1);
            for(int i = idsGrpSel.length+2; i <= (idsGrpSel.length+idsEnsSel.length); i++)
                critEns += (", ?"+i);
            critEns += ")";

            if(idsGrpSel.length > 0 && idsEnsSel.length > 0)
                strQ += ("AND ("+critGrp+" OR "+critEns+")");
            else if(idsGrpSel.length > 0)
                strQ += ("AND "+critGrp);
            else if(idsEnsSel.length > 0)
                strQ += ("AND "+critEns);

            Query q = em.createQuery(strQ);
            for(int i = 1; i <= idsGrpSel.length; i++)
                q.setParameter(i, idsGrpSel[i-1]);
            for(int i = (idsGrpSel.length+1); i <= (idsGrpSel.length+idsEnsSel.length); i++)
                q.setParameter(i, idsEnsSel[i-idsGrpSel.length-1]);

            return q.getResultList();
        } finally {
            em.close();
        }
    }

    /**
     * Récupère la liste des prestations qui se trouve sur l'Emploi Du Temps
     * affiché (c'est-à-dire celui pour lequel des groupes ou des enseignants
     * ont été sélectionnés, les identifiants de ces groupes et enseignants
     * sont passés en paramètres).
     * @param idsGrpSel identifiants des groupes sélectionnés pour apparaître sur l'EDT.
     * @param idsEnsSel identifiants des enseignants sélectionnés pour apparaître sur l'EDT.
     * @return liste des prestations qui doivent apparaître sur l'EDT (avec
     * les modules, les groupes et les enseignants préchargés).
     */
    public List<Prestation> findPrestationsSurEDT(int[] idsGrpSel, int[] idsEnsSel) {
        return this.findPrestationsRelativesEDT(idsGrpSel, idsEnsSel, true);
    }

    /**
     * Récupère la liste des prestations qui n'ont pas encore de créneau mais
     * qui pourraient se trouver sur l'Emploi Du Temps affiché (c'est-à-dire
     * celui pour lequel des groupes ou des enseignants ont été sélectionnés,
     * les identifiants de ces groupes et enseignants sont passés en paramètres).
     * @param idsGrpSel identifiants des groupes sélectionnés pour apparaître sur l'EDT.
     * @param idsEnsSel identifiants des enseignants sélectionnés pour apparaître sur l'EDT.
     * @return liste des prestations qui ne sont pas encore sur l'EDT (avec
     * les modules, les groupes et les enseignants préchargés).
     */
    public List<Prestation> findPrestationsNonCasees(int[] idsGrpSel, int[] idsEnsSel) {
        return this.findPrestationsRelativesEDT(idsGrpSel, idsEnsSel, false);
    }

    /**
     * Récupère les prestations d'un module triée par ordre d'insertion.
     * @param module
     * @return
     */
    public List<Prestation> findPrestationsParModule(Module module) {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT p " +
                          "FROM Prestation AS p " +
                          "WHERE p.idModule = :module "+
                          "ORDER BY p.id";
            Query q = em.createQuery(strQ);
            q.setParameter("module", module);
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    /**
     * Enlève la prestation à remplacer de son créneau et met la prestation
     * à placer dans ce créneau.
     * @param prestAPlacer
     * @param prestARemplacer
     * @throws gestionedt.controllers.exceptions.NonexistentEntityException
     * @throws java.lang.Exception
     */
    public void interchanger(Prestation prestAPlacer, Prestation prestARemplacer)
     throws NonexistentEntityException, Exception {
        EntityManager em = getEntityManager();
        try {
            em.getTransaction().begin();
            Creneau c = prestARemplacer.getCreneau();
            prestARemplacer.setCreneau(null);
            edit(prestARemplacer);
            prestAPlacer.setCreneau(c);
            edit(prestAPlacer);
            em.getTransaction().commit();
        } finally {
            em.close();
        }
    }

    public boolean suitPrestation(Groupe groupe, Creneau creneau) {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT COUNT(DISTINCT p) " +
                          "FROM Prestation AS p " +
                            "INNER JOIN p.groupeCollection g " +
                          "WHERE g = :groupe AND p.numCreneau = :creneau ";
            Query q = em.createQuery(strQ);
            q.setParameter("groupe", groupe);
            q.setParameter("creneau", creneau);

            return ((Long)q.getSingleResult() > 0);
        } finally {
            em.close();
        }
    }

    public boolean assurePrestation(Enseignant enseignant, Creneau creneau) {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT COUNT(DISTINCT p) " +
                          "FROM Prestation AS p " +
                          "WHERE p.idEnseignant = :enseignant AND p.numCreneau = :creneau ";
            Query q = em.createQuery(strQ);
            q.setParameter("enseignant", enseignant);
            q.setParameter("creneau", creneau);

            return ((Long)q.getSingleResult() > 0);
        } finally {
            em.close();
        }
    }

    /**
     * Récupère une liste de prestations avec leurs relations préchargées.
     * @param idPrestation paramètre facultatif, s'il vaut null toutes les prestations seront récupérées
     * @return
     */
    private List<Prestation> findAvecRel(Integer idPrestation) {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT DISTINCT p " +
                          "FROM Prestation AS p " +
                            "LEFT JOIN FETCH p.idModule " +
                            "LEFT JOIN FETCH p.idEnseignant " +
                            "LEFT JOIN FETCH p.numCreneau " +
                            "LEFT JOIN FETCH p.groupeCollection ";
            if(idPrestation != null)
                strQ += "WHERE p.id = :idPrestation ";

            Query q = em.createQuery(strQ);
            q.setHint(QueryHints.REFRESH, HintValues.TRUE);
            if(idPrestation != null)
                q.setParameter("idPrestation", idPrestation);

            return q.getResultList();
        } finally {
            em.close();
        }
    }

    /**
     * Récupère une liste de prestations avec leurs relations préchargées.
     * @return
     */
    public List<Prestation> findPrestationsAvecRel() {
        return this.findAvecRel(null);
    }

    /**
     * Recupère une prestation avec ses relations préchargées.
     * @param idPrestation
     * @return
     */
    public Prestation findPrestationAvecRel(Integer idPrestation) {
        List<Prestation> liste = findAvecRel(idPrestation);
        return (liste.size() > 0 ? liste.get(0) : null);
    }

    /**
     * Retourne la liste complète des prestations qui ne sont pas encore
     * placées sur l'emploi du temps.
     * @return
     */
    public List<Prestation> findPrestationsNonPlacees() {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT DISTINCT p " +
                          "FROM Prestation AS p " +
                            "LEFT JOIN p.idModule m " +
                            "LEFT JOIN FETCH p.idModule " +
                            "LEFT JOIN FETCH p.idEnseignant " +
                            "LEFT JOIN FETCH p.groupeCollection " +
                          "WHERE p.numCreneau IS NULL AND m.estTermine = FALSE " +
                          "ORDER BY p.idModule.libelle";
            Query q = em.createQuery(strQ);
            q.setHint(QueryHints.REFRESH, HintValues.TRUE);
            return q.getResultList();
        } finally {
            em.close();
        }
    }

}
